<?php
namespace slinks\common\io;

class ClassFileSystemVisitor implements IFileSystemVisitor{

	const NEXT_CLASS        = 'class';
    const NEXT_NAMESPACE    = 'namespace';
    const NEXT_USE			= 'use';
    const NEXT_NOTHING      = 'nothing';
	
	/* @var $classVisitor IClassVisitor */
	private $classVisitor = null;
	
	
	/**
	 * Constructs a new ClassFileSystemVisitor with the given IClassVisitor
	 * @param IClassVisitor $classVisitor the class visitor to be called when reading the classes from the filesystem
	 */
	public function __construct(IClassVisitor $classVisitor){
		$this->classVisitor = $classVisitor;
	}


	public function visit(\SplFileInfo $info){
		// we only need to process files so 
		if($info->isFile()){	
			/* @var $splFileObject SplFileObject */
			$splFileObject = $info->openFile();
	
			$contents = null;
			while( !$splFileObject->eof() ){
				$contents .= $splFileObject->fgets();
			}
			
			$classes = $this->getClassesInSource($contents);
			
			foreach($classes as $class){
				$this->classVisitor->visit($info, $class);
			}
		} 
	}
	
    /**
     * Returns the classes in the code $source
     *
     * getClassesInSource() uses the tokenizer to return
     * a list of class definitions.
     *
     * @param String $source A source which might contain class definitions
     *
     * @throws Exception
     * @return Array found classes in the source
     */
    private function getClassesInSource($source)
    {
        $classes        = array();
        $nextStringType = self::NEXT_NOTHING;
        $namespace      = '';
        $tokens         = @token_get_all($source);

        if (! is_array($tokens)) {
            $error = error_get_last();
            throw new \Exception(
                "Could not tokenize: $error[message]\n$source");
        }

        foreach ($tokens as $token) {
            if (! is_array($token)) {
                continue;
            }
            $tokenID    = $token[0];
            $tokenValue = $token[1];

            switch ($tokenID) {
            case T_NAMESPACE:
                $namespace      = '';
                $nextStringType = self::NEXT_NOTHING;
            case T_NS_SEPARATOR:
            	if($nextStringType != self::NEXT_USE){
                	$nextStringType = self::NEXT_NAMESPACE;
            	}
                break;
            case T_INTERFACE:
            case T_CLASS:
                $nextStringType = self::NEXT_CLASS;
                break;
			case T_USE:
				$nextStringType = self::NEXT_USE;
				break;
            case T_STRING:
                $type           = $nextStringType;
                if($nextStringType != self::NEXT_USE){
                	$nextStringType = self::NEXT_NOTHING;
                }
                switch ($type) {
                case self::NEXT_CLASS:
                    $classes[] = $namespace.$tokenValue;
                    break;

                case self::NEXT_NAMESPACE:
                    $namespace .= "$tokenValue\\";
                    break;
                }
                break;
            }
        }
        return $classes;
    }

}

